require_relative './amplify_pods.rb'

opt_out_usage
default_platform(:ios)

PLIST_KEY = "CFBundleShortVersionString"

platform :ios do
  before_all do 
    # Perform a fetch before inferring the next version 
    # to reduce race conditions with simultaneous pipelines attempting to create the same tag
    sh('git', 'fetch', '--tags')
    sh('git', 'fetch')
  end
  desc "Create a pre-release version by pushing a tag to GitHub, and pushing pods to CocoaPods"
  lane :unstable do 
    next_version = calculate_next_canary_version

    UI.message("Releasing unstable version: #{next_version}")

    # Increment all specs and plists
    increment_versions(version: next_version)

    # Create tag and push to origin
    add_tag(version: next_version)

    # Push to CocoaPods
    release_pods()
  end

  desc "Create a release version by building and committing a changelog, pushing a tag to GitHub, and pushing pods to CocoaPods"  
  lane :release do
    next_version, commits = calculate_next_release_version

    UI.message("Releasing version: #{next_version}")

    # Increment all specs and plists
    increment_versions(version: next_version)

    changelog = build_changelog(version: next_version, commits: commits)

    # Commit and push 
    release_commit(version: next_version)

    # Create tag and push to origin
    add_tag(version: next_version)

    # Push pods
    release_pods()

    post_release(version: next_version, changelog: changelog)
  end

  desc "Increment versions"
  private_lane :increment_versions do |options|
    version = options[:version].to_s

    AmplifyPods.pods.each do |pod|
      spec, constants, plist_paths = pod.values

      UI.message("Incrementing version for #{spec}")

      constants.each do |constant|
        set_key_value(file: spec, key: constant, value: version)
      end

      plist_paths.each do |plist_path|
        set_info_plist_value(path: plist_path, key: PLIST_KEY, value: version)
      end

      # One-offs
      set_key_value(file: "AmplifyPlugins/Core/AWSPluginsCore/ServiceConfiguration/AmplifyAWSServiceConfiguration.swift", key: "version", value: version)
      set_key_value(file: "build-support/dependencies.rb", key: "AMPLIFY_VERSION", value: version)
    end
  end
  
  desc "Commit and push"
  private_lane :release_commit do |options|
    next_version = options[:version]

    sh('git', 'config', '--global', 'user.email', ENV['GITHUB_EMAIL'])
    sh('git', 'config', '--global', 'user.name', ENV['GITHUB_USER'])

    commit_message = "chore: release #{next_version} [skip ci]"
    sh('git', 'commit', '-am', commit_message)

    # push to origin
    sh('git', 'push', 'origin', 'release')
  end
  
  desc "Tag in git and push to GitHub"
  private_lane :add_tag do |options|
    next_version = options[:version]
    next_tag = "v#{next_version}"

    add_git_tag(tag: next_tag)
    push_git_tags(tag: next_tag)
  end

  desc "Release pods"
  private_lane :release_pods do
    AmplifyPods.pods.each do |pod|
      if pod[:no_push]
        UI.message "Skipping pushing pod #{pod[:spec]}"
        next
      end

      UI.message "Pushing pod #{pod[:spec]}"

      with_retry do
        begin
          pod_push(path: pod[:spec], allow_warnings: true, synchronous: true)
        rescue StandardError => e
          raise e unless e.message =~ /Unable to accept duplicate entry/i
          UI.important("Version already exists. Ignoring")
        end
      end

      unless pod == AmplifyPods.pods.last
        # This shouldn't be necessary... but it is
        sh('bundle', 'exec', 'pod', 'repo', 'update')
      end
    end
  end

  desc "Post-release"
  private_lane :post_release do |options|
    version = options[:version].to_s
    changelog = options[:changelog]
    tag = "v#{version}"
    plugin_root = File.expand_path("#{ENV['CIRCLE_WORKING_DIRECTORY']}/AmplifyPlugins")
  
    sh('bundle', 'exec', 'swift', 'package', 'update')

    sh('bundle', 'exec', 'pod', 'repo', 'update')

    # increment plugin lock files by calling pod update on each
    AmplifyPods.plugins.each do |plugin|
      path = "#{plugin_root}/#{plugin}"
      Dir.chdir(path){ sh('pod', 'update') }
    end

    write_changelog(changelog: changelog, path: 'CHANGELOG.md')

    commit_message = "chore: finalize release #{version} [skip ci]"
    sh('git', 'commit', '-am', commit_message)

    add_git_tag(tag: tag, force: true)
    push_git_tags(tag: tag, force: true)

    # push to origin
    sh('git', 'push', 'origin', 'release')
    sh('git', 'push', 'origin', 'release:main')
  end
end

def with_retry(retries=5, wait=60)
  begin
    yield
  rescue StandardError => e
    retries -= 1
    raise e if retries.zero?
      UI.error("Error occurred: #{e}; retrying...")
      sleep(wait)
    retry
  end
end
